package com.zxyankh.raft.service.impl;

import com.zxyankh.raft.context.ServerContext;
import com.zxyankh.raft.dto.*;
import com.zxyankh.raft.service.LogService;
import com.zxyankh.raft.service.RaftService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import javax.annotation.PostConstruct;

/**
 * @author ankh
 * @since 31.10.2017
 */
@Service
public class RaftServiceImpl implements RaftService {

    @Autowired
    private LogService logService;

    @PostConstruct
    private void init() {

    }

    @Override
    public void addCommand(String command) {

    }

    @Override
    public Entry getCommand(int index) {
        return null;
    }

    /**
     * 处理RequestVoteRPC
     *
     * @param requestVote 请求实体
     * @return RequestVoteResponse
     */
    private RequestVoteResponse handleRequestVote(RequestVote requestVote) {
        RequestVoteResponse requestVoteResponse = new RequestVoteResponse();
        // 如果term<currentTerm则回复false
        if (requestVote.getTerm() < ServerContext.currentTerm.get()) {
            requestVoteResponse.setTerm(ServerContext.currentTerm.get());
            requestVoteResponse.setVoteGranted(false);
            return requestVoteResponse;
        }
        // 如果votedFor是空的或者candidatedId，且候选者的日志最少是和接收者的日志是最新的，则投票
        if ((ServerContext.votedFor == -1 || ServerContext.votedFor == requestVote.getCandidateId())
                && requestVote.getLastLogIndex() == ServerContext.commitIndex
                && requestVote.getLastLogTerm() == ServerContext.currentTerm.get()) {
            requestVoteResponse.setTerm(ServerContext.currentTerm.get());
            requestVoteResponse.setVoteGranted(true);
        }
        return requestVoteResponse;
    }

    /**
     * 处理AppendEntries的Heartbeat RPC
     * 判断条件是entries数组为空数组
     *
     * @param appendEntries 请求实体
     * @return AppendEntriesResponse
     */
    private AppendEntriesResponse handleHeartbeat(AppendEntries appendEntries) {
        AppendEntriesResponse appendEntriesResponse = new AppendEntriesResponse();
        appendEntriesResponse.setTerm(ServerContext.currentTerm.get());
        Entry prevLogEntry = ServerContext.log.get(appendEntries.getPrevLogIndex());
        if (appendEntries.getPrevLogIndex() == prevLogEntry.getIndex()
                && prevLogEntry.getTerm() == appendEntries.getPrevLogTerm()) {
            appendEntriesResponse.setSuccess(true);
        } else {
            appendEntriesResponse.setSuccess(false);
        }
        return appendEntriesResponse;
    }

    /**
     * 处理AppendEntries的非Heartbeat RPC
     * 判断条件是entries数组为非空数组
     *
     * @param appendEntries 请求实体
     * @return AppendEntriesResponse
     */
    private AppendEntriesResponse handleAppendEntries(AppendEntries appendEntries) {
        AppendEntriesResponse appendEntriesResponse = new AppendEntriesResponse();
        // 如果term<currentTerm返回false
        if (appendEntries.getTerm() < ServerContext.currentTerm.get()) {
            appendEntriesResponse.setTerm(ServerContext.currentTerm.get());
            appendEntriesResponse.setSuccess(false);
            return appendEntriesResponse;
        }
        // 如果日志在prevLogIndex上没有实体的term为prevLogTerm，返回false
        if (ServerContext.log.get(appendEntries.getPrevLogIndex()).getTerm() != appendEntries.getPrevLogTerm()) {
            appendEntriesResponse.setTerm(ServerContext.currentTerm.get());
            appendEntriesResponse.setSuccess(false);
            return appendEntriesResponse;
        }
        // 如果已有的实体和新的冲突（相同索引但是不同term），删除已有的实体和它之后的实体
        int size = ServerContext.log.size();
        boolean cleared = false;
        for (Entry entry : appendEntries.getEntries()) {
            if (entry.getIndex() < size && !cleared) {
                Entry existsLogEntry = ServerContext.log.get(entry.getIndex());
                if (existsLogEntry == null) {
                    // 添加任意不在日志中的实体
                    ServerContext.log.add(entry);
                } else if (existsLogEntry.getIndex() == entry.getIndex()
                        && existsLogEntry.getTerm() != entry.getTerm()) {
                    for (int i = entry.getIndex(); i < size; i++) {
                        ServerContext.log.remove(i);
                    }
                    cleared = true;
                }
            } else {
                // 添加任意不在日志中的实体
                ServerContext.log.add(entry);
            }
        }
        // 如果leaderCommit>commitIndex，设置commitIndex=min(leaderCommit, 最新实体的索引)
        if (appendEntries.getLeaderCommit() > ServerContext.commitIndex) {
            ServerContext.commitIndex = Math.min(appendEntries.getLeaderCommit(),
                    ServerContext.log.get(ServerContext.log.size() - 1).getIndex());
        }
        return appendEntriesResponse;
    }
}
